Sign only the parameters Cloudinary verifies, unbreaking uploads - #187
Merged
Conversation
Photo upload has been failing with Invalid Signature. The server signed four parameters; Cloudinary verifies three. Cloudinary signs only the upload parameters it recognises and silently drops the rest. max_file_size is not one it recognises, so including it in the signed set produced a signature that could never match. Its own error said so — the String to sign it echoed back listed folder, timestamp and upload_preset, and nothing else. This is not a regression from #185, which never touched media.ts or the client upload path. It dates from whenever max_file_size entered the signature and has been broken ever since; nobody noticed because nobody had uploaded a photo through the UI in between. The comment above the limits is what caused this. It claimed they were "enforced on the signature itself so a leaked signature can't be reused to upload a bigger file than we allow" — a reasonable-sounding guarantee that Cloudinary does not offer. Replaced with what is actually true: the limits are advisory, they exist to feed the client-side check, and the only enforceable ceiling is whatever the petnote_image_signed / petnote_video_signed upload presets have configured in the Cloudinary console. The client stops sending max_file_size too. Cloudinary ignores it, so it bought nothing except the appearance that the limit was being transmitted somewhere meaningful. The size check that produces a useful error before a doomed upload stays, and the callable still returns maxFileSize to feed it. Two tests encoded the same false premise and are corrected. One of them — "signs exactly the parameters it returns" — already carried the comment "if the handler ever signs a different set of params than it hands back, Cloudinary rejects every upload". It was right, and it still passed, because it recomputed the same wrong set the handler used. max_file_size was on both sides of that comparison and on neither side of Cloudinary's. Its list is now written as the contract with Cloudinary rather than a mirror of the implementation. NOT VERIFIED HERE: whether the upload presets actually carry a max file size in the Cloudinary console. I cannot read that from the repo. If no limit is set there, then after this change there is no server-side size ceiling at all — only the advisory client check. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Sep 6, 2026
renrenmimi
added a commit
that referenced
this pull request
Sep 7, 2026
The comment I left in #187 was still wrong, just wrong in a new direction. It said the enforceable limit lived in the petnote_image_signed / petnote_video_signed upload presets. The owner checked the Cloudinary console: a preset has no max-file-size setting at all, across all six tabs. That absence is precisely why the parameter was silently ignored and the signature could never match. The real ceiling is the account plan. Cloudinary rejects anything over it regardless of what we send — on the free tier, 10 MB for images and 100 MB for video. The constants here were chosen to match it: the image number IS the plan limit, and the video number is deliberately stricter. So a server-side ceiling has existed all along. It is just enforced by the platform rather than by us, which is a different claim from either comment this file has carried. Written as a table of where a limit can and cannot be enforced — plan, preset, signature — so the next person can see at a glance that the signature is not an option, rather than reasoning their way back to the bug. Ends with the rule outright: the signature proves the request came from us, it cannot constrain the upload. Comment only; no behaviour change. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Photo upload has been failing with
Invalid Signature. The server signed four parameters; Cloudinary verifies three.Cloudinary signs only the upload parameters it recognises and silently drops the rest.
max_file_sizeis not one of them, so including it produced a signature that could never match. Its own error said so — theString to signit echoed back listedfolder,timestamp,upload_preset, and nothing else.Not a regression from #185. That PR never touched
media.tsor the client upload path. This dates from whenevermax_file_sizeentered the signature and has been broken ever since; nobody noticed because nobody had uploaded a photo through the UI in between.The comment is what caused this
A reasonable-sounding guarantee that Cloudinary does not offer. Someone read it and wired the limit into the signature. It's replaced with what's true: the limits are advisory, they feed the client-side check, and the only enforceable ceiling is whatever the upload presets have configured in the Cloudinary console.
The client stops sending
max_file_sizetoo — Cloudinary ignores it, so it bought nothing but the appearance that the limit went somewhere meaningful. The size check that gives a useful error before a doomed upload stays, and the callable still returnsmaxFileSizeto feed it.The test that should have caught this, and why it didn't
"signs exactly the parameters it returns"already carried this comment:It was right. It also passed — because it recomputed the same wrong set the handler used.
max_file_sizewas on both sides of that comparison and on neither side of Cloudinary's. A test that mirrors the implementation cannot catch the implementation being wrong about a third party.Its list is now written as the contract with Cloudinary rather than a copy of what the handler does, with a note not to add a parameter without confirming Cloudinary signs it. The sibling test that claimed the limit was "bound into the signature" is retitled and its comment corrected.
Two things I could not verify
1. Whether the upload presets carry a max file size. I cannot read the Cloudinary console from the repo. If
petnote_image_signed/petnote_video_signedhave no limit configured, then after this change there is no server-side size ceiling at all — only the advisory client check, which a crafted request skips. Worth checking before launch; if it's unset, that's a separate PR to set it.2. That uploads actually work again. Local
lintandbuildpass for both trees, and CI runs the emulator suite here — but none of that touches Cloudinary. This is precisely the class of bug CI cannot catch, which is the whole point of this PR. A real upload has to happen after deploy before this is called done.🤖 Generated with Claude Code